/**
 * ============================================================================
 *
 * Copyright (C) 2018, Hisilicon Technologies Co., Ltd. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1 Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *
 *   2 Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *
 *   3 Neither the names of the copyright holders nor the names of the
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * ============================================================================
 */


#include <memory>
#include <fstream>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <cmath>
#include <regex>
#include "hiaiengine/log.h"
#include "hiaiengine/data_type_reg.h"
#include "style_transfer_params.h"
#include "style_type_register_listen.h"
#include "style_type_register.h"

using hiai::Engine;
using namespace hiai;
using namespace std;
using namespace ascend::presenter;

HIAI_REGISTER_DATA_TYPE("StyleTypeRegisterData", StyleTypeRegisterData);

bool StyleTypeRegister::IsInvalidIp(const string &ip) {
    regex re(kIpRegularExpression);
    smatch sm;
    return !regex_match(ip, sm, re);
}

bool StyleTypeRegister::IsInvalidPort(const string &port) {
    if (port == "") {
        return true;
    }
    for (auto &c : port) {
        if (c >= '0' && c <= '9') {
            continue;
        } else {
            return true;
        }
    }
    int port_tmp = atoi(port.data());
    return (port_tmp < kPortMinNumber) || (port_tmp > kPortMaxNumber);
}

bool StyleTypeRegister::IsInvalidAppName(const string &app_name) {
    regex re(kAppNameRegularExpression);
    smatch sm;
    return !regex_match(app_name, sm, re);
}

StyleTypeRegister::~StyleTypeRegister() {
  if (agent_channel_ != nullptr) {
    delete agent_channel_;
  }
}

HIAI_StatusT StyleTypeRegister::Init(const hiai::AIConfig &config,
                                const std::vector<hiai::AIModelDescription> &model_desc) {
    PresenterServerParams app_config;

    // get engine config and copy to app_config
    for (int index = 0; index < config.items_size(); index++) {
        const ::hiai::AIConfigItem& item = config.items(index);
        string name = item.name();
        string value = item.value();

        if (name == kPresenterServerIP) {
            // validate presenter server IP
            if (IsInvalidIp(value)) {
                HIAI_ENGINE_LOG(HIAI_GRAPH_INVALID_VALUE,
                                "host_ip = %s which configured is invalid.",
                                value.c_str());
                return HIAI_ERROR;
            }
            app_config.host_ip = value;
        } else if (name == kPresenterServerPort) {
            // validate presenter server port
            if (IsInvalidPort(value)) {
                HIAI_ENGINE_LOG(HIAI_GRAPH_INVALID_VALUE,
                                "port = %s which configured is invalid.",
                                value.c_str());
                return HIAI_ERROR;
            }
            app_config.port = atoi(value.data());
        } else if (name == kAppName) {
            // validate app name
            if (IsInvalidAppName(value)) {
                HIAI_ENGINE_LOG(HIAI_GRAPH_INVALID_VALUE,
                                "app_name = %s which configured is invalid.",
                                value.c_str());
                return HIAI_ERROR;
            }
            app_config.app_id = value;
        } else {
            HIAI_ENGINE_LOG("unused config name: %s", name.c_str());
        }
    }
    app_config.app_type = kAppType;
    HIAI_ENGINE_LOG("host_ip = %s, port = %d,app_name = %s",
                    app_config.host_ip.c_str(), app_config.port,
                    app_config.app_id.c_str());

    // create agent channel by host_ip and port
    // Init class PresenterChannels by configs
    PresenterChannels::GetInstance().Init(app_config);

    // create agent channel and register app
    Channel* agent_channel = PresenterChannels::GetInstance().GetChannel();
    if (agent_channel == nullptr) {
        HIAI_ENGINE_LOG(HIAI_ENGINE_RUN_ARGS_NOT_RIGHT, "register app failed.");
        return HIAI_ERROR;
    }
    StartStyleTypeRegisterListen(agent_channel);
    HIAI_ENGINE_LOG("styletype_register ok");
    HIAI_ENGINE_LOG(HIAI_ENGINE_RUN_ARGS_NOT_RIGHT, "style type register Init ok");

    return HIAI_OK;
}

bool StyleTypeRegister::DoRegisterProcess(shared_ptr<StyleTypeRegisterData> style_register_info) {
   // get agent channel

    HIAI_StatusT hiai_ret = HIAI_OK;
    HIAI_ENGINE_LOG("start  process register req\n");
    //  construct registered request Message and read message
    ascend::presenter::style_transfer::StyleTypeInfo* style_register_req = 
        (ascend::presenter::style_transfer::StyleTypeInfo*) (style_register_info->response_rec.get());
    if (style_register_req == nullptr) {
        HIAI_ENGINE_LOG(HIAI_ENGINE_RUN_ARGS_NOT_RIGHT,
                        "[DoRegisterProcess]style_register_req is nullptr");
        return false;
    }

    std::string style_type_id =  style_register_req->type_id();
    uint32_t changed_id = atoi(style_type_id.c_str());
    HIAI_ENGINE_LOG(HIAI_ENGINE_RUN_ARGS_NOT_RIGHT, "[DoRegisterProcess]style_register id:%d", changed_id);

    // ready to send registered info to next engine, 
    // Here only string style_type_id is needed to send the next engine.
    // shared_ptr <string> pobj = make_shared<string>(style_type_id);
    shared_ptr < StyleType > pobj = make_shared<StyleType>();
    pobj->name = "style_transfer";
    pobj->type = changed_id;

    //  send registered infomation to next engine
    hiai_ret = SendData(0, "StyleType", static_pointer_cast<void>(pobj));
    if (hiai_ret != HIAI_OK) {
        HIAI_ENGINE_LOG(HIAI_ENGINE_RUN_ARGS_NOT_RIGHT, "[StyleTypeRegister] SendData failed.");      
        return false;
    }
    HIAI_ENGINE_LOG("Process style type register successful");

    return  true;
}

HIAI_IMPL_ENGINE_PROCESS("style_type_register", StyleTypeRegister, INPUT_SIZE)
{
    shared_ptr<StyleTypeRegisterData> style_register_info = static_pointer_cast <StyleTypeRegisterData > (arg0);
    HIAI_ENGINE_LOG("styletype_register receive register  req");
    if  (!DoRegisterProcess(style_register_info)) {
    HIAI_ENGINE_LOG(HIAI_ENGINE_RUN_ARGS_NOT_RIGHT,
        "DoRegisterProcess failed.");
    }
    HIAI_ENGINE_LOG("styletype_register process end\n");
    return  HIAI_OK;
}
